package service

import (
	"advertising/config/mysql"
	"advertising/internal/app/grpc/client"
	"advertising/internal/app/grpc/protoc/system_admin"
	"advertising/internal/dao"
	"advertising/internal/define"
	"advertising/internal/models"
	"advertising/internal/pkg"
	"context"
	"os"
	"path"
	"strconv"
	"strings"
	"unicode/utf8"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"gorm.io/gorm"
)

type Material struct{}

// Save 素材保存
func (Material) Save(ctx context.Context, db *gorm.DB, merchantID, merchantName, userID, username, name, filePath string, position, Type int8, folderID string) (id, newFilePath string, err error) {
	var Duration int64
	var Size string
	var fileSize int64
	if strings.TrimSpace(name) == "" {
		err = errorx.New("素材名称不能为空", -1)
		return
	}
	if utf8.RuneCountInString(strings.TrimSpace(name)) > 30 {
		err = errorx.New("素材名称不能超过30字符", -1)
		return
	}
	nameExist := dao.Material{}.SameNameExist(ctx, db, merchantID, name, "")
	if nameExist > 0 {
		err = errorx.New("素材名称已经存在", -1)
		return
	}
	if Type == 0 {
		err = errorx.New("素材类型不能为空", -1)
		return
	}
	if Type != 1 && Type != 2 {
		err = errorx.New("素材类型错误", -1)
		return
	}

	// 文件夹ID校验
	if folderID == "" {
		folderID = "0"
	}
	if folderID != "0" {
		// 文件夹是否存在
		folderInfo, _ := dao.MaterialFolder{}.Detail(ctx, db, merchantID, folderID, Type)
		if folderInfo.ID == "" {
			return "", "", define.ErrGroupNoExist
		}
	}

	if Type == 1 { //图片
		if position == 0 {
			err = errorx.New("图片素材所属位置不能为空", -1)
			return
		}
		if position != 1 && position != 2 {
			err = errorx.New("图片素材所属位置错误", -1)
			return
		}
		if filePath == "" {
			err = errorx.New("图片不能为空", -1)
			return
		}
		//图片 2M .jpg,.png,.jpeg
		if !(path.Ext(filePath) == ".jpg") && !(path.Ext(filePath) == ".png") && !(path.Ext(filePath) == ".jpeg") {
			err = errorx.New("图片格式错误", -1)
			return
		}
		fileInfo, err1 := os.Stat(filePath)
		if err1 != nil {
			err = errorx.New("读取文件错误", -1)
			return
		}
		fileSize = fileInfo.Size()
		size := pkg.Decimal(float64(fileSize)/(1024*1024), 2) //MB
		if size > 2 {                                         //2M
			err = errorx.New("图片最大2M", -1)
			return
		}
		Size = strconv.FormatFloat(size, 'f', -1, 64) + "MB"
	} else if Type == 2 { //视频
		if filePath == "" {
			err = errorx.New("视频不能为空", -1)
			return
		}
		if !(path.Ext(filePath) == ".mp4") {
			err = errorx.New("视频格式错误", -1)
			return
		}
		//视频判断时长 [4-300]s
		duration, err1 := pkg.GetMP4Duration(filePath)
		if err1 != nil {
			logx.Error(context.Background(), "get video duration", logx.Any("error", err1.Error()))
			err = err1
			return
		}
		if duration < 4 || duration > 240 {
			err = errorx.New("视频时长最短4秒,最长240秒", -1)
			return
		}
		Duration = duration
		//文件大小 20M
		fileInfo, err1 := os.Stat(filePath)
		if err1 != nil {
			err = errorx.New("读取文件错误", -1)
			return
		}
		fileSize = fileInfo.Size()
		size := pkg.Decimal(float64(fileSize)/(1024*1024), 2) //MB
		if size > 200 {                                       //200M
			err = errorx.New("视频最大200M", -1)
			return
		}
		Size = strconv.FormatFloat(size, 'f', -1, 64) + "MB"
		position = 2 // 视频默认后屏
	}
	//文件确认
	SystemAdminClient, clientErr := client.GetSystemAdminClient()
	if clientErr != nil {
		logx.Error(ctx, "material save system admin error", logx.Any("err", clientErr.Error()))
		err = errorx.New("图片上传失败", -1)
		return
	}
	var req system_admin.ConfirmFileReq
	req.FileUrl = filePath
	res, err1 := SystemAdminClient.ConfirmFile(context.Background(), &req)
	if err1 != nil && res.Code != 0 {
		logx.Error(context.Background(), "material confirm file error", logx.Any("res", res), logx.Any("err", err1.Error()))
		return
	}
	filePath = res.FileUrl

	md5 := helper.Md5File(filePath)

	mid, err := dao.Material{}.Create(ctx, db, name, Type, folderID, position, Duration, filePath, Size, fileSize, md5, merchantID, merchantName, userID, username)
	return mid, filePath, err
}

// List 素材列表
func (Material) List(ctx context.Context, db *gorm.DB, merchantID string, Type int8, name string,
	position int, status int8, page, limit int, folderID string) (data []*models.Material, count int64, err error) {
	TypeString := []int8{Type}
	var Status []int8
	if status != 0 {
		Status = []int8{status}
	}
	MaterialList, count, _ := dao.Material{}.List(ctx, db, merchantID, "", name, TypeString, Status, "" /* createUsername */, position, page, limit, folderID)
	return MaterialList, count, nil
}

// MaterialInUseList 可用素材列表
func (Material) MaterialInUseList(ctx context.Context, db *gorm.DB, merchantID string, Type int8, name string,
	position, page, limit int, folderID string) (data []*models.Material, count int64, err error) {
	TypeString := []int8{Type}
	StatusString := []int8{2} // 2 审核通过可用
	MaterialList, count, _ := dao.Material{}.List(ctx, db, merchantID, "", name, TypeString, StatusString, "" /* createUsername */, position, page, limit, folderID)
	return MaterialList, count, nil
}

// AuditingMaterialCount 审核中素材统计
func (Material) AuditingMaterialCount(ctx context.Context, merchantID string) (count int64) {
	db := mysql.NewDB()
	_, count = dao.Material{}.Detail(ctx, db, merchantID, "", "", 1) // 1 等待审核
	return
}

// Delete 素材删除 [支持批量]
func (Material) Delete(ctx context.Context, db *gorm.DB, merchantID, IDs string) (err error) {
	if strings.TrimSpace(IDs) == "" {
		err = errorx.New("素材不能为空", -1)
		return
	}
	// 判断素材是否被计划使用
	scheduleMaterialList, scheduleMaterialCount, _ := dao.PublishScheduleMaterial{}.List(ctx, db, "", IDs, 0, 0)
	if scheduleMaterialCount > 0 {
		scheduleIDList := []string{}
		for _, v := range scheduleMaterialList {
			scheduleIDList = append(scheduleIDList, v.ScheduleID)
		}
		scheduleList, scheduleCount, _ := dao.PublishSchedule{}.List(ctx, db, merchantID, strings.Join(scheduleIDList, ","), "", "", 0, 0, 0, 0, 0, 0, 0, 0, "", "", "", "", 0, 0)
		if scheduleCount > 0 {
			for _, v := range scheduleList {
				if v.Status != 5 && v.Status != 6 { // 5审核失败,6已结束
					err = errorx.New("素材被占用,请先删除关联的广告计划", -1)
					return
				}
			}
		}
	}

	for _, ID := range strings.Split(IDs, ",") {
		MaterialInfo, count := dao.Material{}.Detail(ctx, db, merchantID, ID, "", 0)
		if count == 0 {
			err = errorx.New("素材不存在", -1)
			return
		}
		if MaterialInfo.Status == 3 {
			err = errorx.New("素材当前处于禁用状态,无法删除", -1)
			return
		}

		//素材文件删除
		os.Remove(MaterialInfo.FilePath)
		dao.Material{}.MaterialDelete(ctx, db, merchantID, ID)
	}
	return
}

// 素材移动文件夹 [支持批量]
func (Material) MoveFolder(ctx context.Context, merchantID, IDs, FolderID string) (err error) {
	db := mysql.NewDB()
	if strings.TrimSpace(IDs) == "" {
		err = errorx.New("素材不能为空", -1)
		return
	}
	var MaterialType int8
	for _, ID := range strings.Split(IDs, ",") {
		MaterialInfo, count := dao.Material{}.Detail(ctx, db, merchantID, ID, "", 0)
		if count == 0 || MaterialInfo.ID == "" {
			err = errorx.New("素材不存在", -1)
			return
		}
		if MaterialType != 0 && MaterialInfo.Type != MaterialType {
			err = errorx.New("素材类型不一致", -1)
			return
		}
		MaterialType = MaterialInfo.Type
	}

	if FolderID != "0" {
		// 文件夹是否存在
		folderInfo, _ := dao.MaterialFolder{}.Detail(ctx, db, merchantID, FolderID, MaterialType)
		if folderInfo.ID == "" {
			return define.ErrGroupNoExist
		}
	}
	dao.Material{}.MaterialUpdateFolder(ctx, db, merchantID, IDs, FolderID)
	return
}
